package com.extremelylargepackage.level;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.extremelylargepackage.FileUtils;
import com.extremelylargepackage.GameResources;
import com.extremelylargepackage.entity.Dialog;
import com.extremelylargepackage.entity.ScrollingBackground;

public abstract class LevelParser {
	private static Level _level;

	private LevelParser()
	{
		throw new IllegalStateException("LevelParser cannot be instantiated");
	}

	public static Level parseLevel(String levelName, boolean local)
			throws SAXException, IOException, ParserConfigurationException, MalformedLevelException
	{
		_level = new Level();

		InputStream levelInput = null;
		
		if(local)
		{
			if(levelName.startsWith(FileUtils.CUSTOM_LEVEL_DIR + "/"))
			{
				levelInput = FileUtils.readCustomLevel(GameResources.getContext(), levelName.substring(6));
			}
			else
			{
				levelInput = FileUtils.readTestLevel(GameResources.getContext());
			}
		}
		else
		{
			levelInput = GameResources.getAssets().open("levels/" + levelName + ".lvl");
		}
		
		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
		
		Document doc = null;
		
		try
		{
			doc = dBuilder.parse(levelInput);
		}
		finally
		{
			levelInput.close();
		}
		
		Element root = doc.getDocumentElement();
		root.normalize();

		if(!root.getNodeName().equals("level")) {
			throw new MalformedLevelException("Root node must be of type \"level\"");
		}
		String bgImage = root.getAttribute("background");
		ScrollingBackground background = new ScrollingBackground(GameResources.getResource(bgImage));
		background.setDirY(Float.parseFloat(root.getAttribute("scrollSpeed")));
		_level.setBackground(background);
		NodeList nodes = root.getChildNodes();
		for(int i = 0; i < nodes.getLength(); ++i) {
			Node n = nodes.item(i);
			if(n.getNodeType() == Node.ELEMENT_NODE) {
				String nodeName = n.getNodeName();

				if(nodeName.equals("wave")) {
					Wave w = Wave.parse((Element) n);
					_level.addEvent(w);
				} else if(nodeName.equals("dialog")) {
					Dialog dialog = Dialog.parse((Element) n);
					_level.addEvent(dialog);
				} else if(nodeName.equals("command")) {
					_level.addEvent(CommandEvent.parseCommandEvent((Element)n));
				}
				else {
					throw new MalformedLevelException("Unrecognized node type \"" + nodeName + "\"");
				}
			}
		}

		return _level;
	}

	/**
	 * Takes a string representing an amount of time (100ms, 10sec) and returns a long
	 * representation of the time in milliseconds.
	 * @param timeStr string to parse
	 * @return time value in ms
	 */
	public static long parseTimeValue(String timeStr)
	{
		String numValue;
		double conversionFactor;

		if(timeStr.endsWith("sec")) {
			conversionFactor = 1000;
			numValue = timeStr.substring(0, timeStr.length() - 3);
		} else if(timeStr.endsWith("ms")) {
			conversionFactor = 1;
			numValue = timeStr.substring(0, timeStr.length() - 2);
		} else {
			throw new IllegalArgumentException("Illegal time value \"" + timeStr + "\"");
		}

		try {
			Float time = Float.parseFloat(numValue);
			return (long) (time * conversionFactor);
		} catch(NumberFormatException nfe) {
			throw new IllegalArgumentException("Illegal time value \"" + timeStr + "\"");
		}
	}
	
	/*
	 *  Parses a String of the form "x,y" converting to game coordinates as well.
	 */
	public static Point parsePoint(String pair)
	{
		Point point = new Point();
		int idxComma = 0;
		for(int i = 0; i < pair.length(); i++)
		{
			if(pair.charAt(i) == ',')
			{
				idxComma = i;
				break;
			}
		}
		float x = Float.parseFloat(pair.substring(0,idxComma));
		float y = Float.parseFloat(pair.substring(idxComma+1, pair.length()));
		point.x = x * GameResources.SCREEN_WIDTH;
		point.y = y * GameResources.SCREEN_HEIGHT;
		return point;
	}
}
